package com.library.config;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Collections;

/**
 * JWT认证过滤器
 * 在每个HTTP请求到达Controller之前，从请求头中提取JWT令牌并验证
 * 验证成功后，将用户信息设置到Spring Security上下文中
 * 
 * 工作流程：
 * 1. 从请求头"Authorization"中提取JWT令牌
 * 2. 验证令牌的有效性（签名、过期时间）
 * 3. 从令牌中提取用户信息
 * 4. 将用户信息设置到SecurityContext中，供后续使用
 */
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 过滤器的核心方法
     * 在每个请求到达Controller之前执行
     * 
     * @param request HTTP请求对象
     * @param response HTTP响应对象
     * @param chain 过滤器链，用于继续执行下一个过滤器
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 1. 从请求头中获取Authorization字段
        // 格式通常是: "Bearer <token>"
        String authHeader = request.getHeader("Authorization");
        
        // 2. 检查是否存在Bearer令牌
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            // 3. 提取实际的令牌（去掉"Bearer "前缀）
            String token = authHeader.substring(7);
            try {
                // 4. 验证令牌的有效性（签名、过期时间）
                if (!jwtUtil.validateToken(token)) {
                    log.warn("Token无效或已过期: {}", request.getRequestURI());
                    // 令牌无效，继续过滤链，但不会设置认证信息
                    // SecurityConfig会拦截未认证的请求
                    chain.doFilter(request, response);
                    return;
                }
                
                // 5. 从令牌中提取用户信息（claims）
                var claims = jwtUtil.extractClaims(token);
                String username = claims.getSubject();  // 用户名
                String role = claims.get("role", String.class);  // 角色
                Long userId = claims.get("id", Long.class);  // 用户ID

                // 6. 如果用户名存在且当前没有认证信息，则设置认证信息
                if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                    // 创建认证令牌，包含用户名和角色权限
                    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                            username, null, Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role)));
                    // 设置请求详情
                    authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    // 将认证信息设置到Spring Security上下文中
                    // 这样后续的Controller就可以通过@RequestAttribute获取用户信息
                    SecurityContextHolder.getContext().setAuthentication(authToken);
                    
                    // 7. 将用户信息设置到request attribute中，方便Controller直接使用
                    request.setAttribute("userId", userId);
                    request.setAttribute("username", username);
                    request.setAttribute("role", role);
                    
                    log.debug("JWT认证成功: 用户={}, 角色={}, URI={}", username, role, request.getRequestURI());
                }
            } catch (Exception e) {
                // 8. 如果验证过程中出现任何异常，记录日志但不中断请求
                log.warn("JWT认证失败: {} - URI={}", e.getMessage(), request.getRequestURI());
                // Token无效，继续过滤链，由SecurityConfig处理未认证请求
            }
        }
        // 9. 继续执行过滤器链（无论是否认证成功）
        chain.doFilter(request, response);
    }
}

